-
-
Notifications
You must be signed in to change notification settings - Fork 14.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ruby-modules/bundler-env: Replace makeWrapper with makeBinaryWrapper to make bundled ruby apps work on macOS #161298
Conversation
…to make bundled ruby apps work on macOS
Result of 3 packages built:
|
This is definitively interesting but it seems to have some possible big implications (e.g.: all Ruby applications). I can merge it but I definitively want someone else to review this. @bergkvist Are you planning similar changes for other interpreted languages (e.g.: Python) 🤔 ? |
@thiagokokada Yes, I would like to change this for most interpreter wrappers in the future, including Python - so that the experience on macOS is significantly improved. I was considering doing multiple interpreters in this PR initially, but decided to instead do this one at a time, since each such change will affect a lot of packages. |
@@ -118,7 +118,7 @@ let | |||
|
|||
wrappedRuby = stdenv.mkDerivation { | |||
name = "wrapped-ruby-${pname'}"; | |||
nativeBuildInputs = [ makeWrapper ]; | |||
nativeBuildInputs = [ makeBinaryWrapper ]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nativeBuildInputs = [ makeBinaryWrapper ]; | |
nativeBuildInputs = if stdenv.isDarwin [ makeBinaryWrapper ] else [ makeWrapper ]; |
I don't think we should convert all wrappers to binary ones just because Darwin is to limited to support basic things. If we would do it, debugging on Linux could become a bit of a pain sometimes. The wrapper includes the inputs but I don't want to think about the edge cases and possible breakages right now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While I think that we should be cautious about this kinda of change to avoid breakage, I still think this has benefits for Linux systems too. For example, reducing the dependency tree to bootstrap (see #160323, where a failure to build bash on Android is causing freetype build to fail), and possible binaries that starts faster (see #124556 (comment) and #124556 (comment), yeah, this is macOS results but I also expect Linux to have some similar improvements).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think BSD-systems also have the same shebang issue as macOS, without having FreeBSD/OpenBSD etc readily available to test this on.
By branching here on platform, if someone using Linux adds a --run
-flag (or another flag which is supported by makeWrapper but not makeBinaryWrapper), they could break the wrappers on macOS without noticing and without breaking them on Linux.
By using the same wrapper type on both macOS and Linux, it means a change that causes a breakage on one platform will likely do so on other platforms as well - decreasing the chance that someone using Linux will break a package for macOS without noticing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While I think that we should be cautious about this kinda of change to avoid breakage, I still think this has benefits for Linux systems too. For example, reducing the dependency tree to bootstrap (see #160323, where a failure to build bash on Android is causing freetype build to fail)
No, bash didn't fail to build there. It is most likely a splicing problem that a binary for a another platform is used and this shouldn't be changed by this PR. Also I think it is safe to assume that we cannot get bash out of bootstraping something. There is always some bash somewhere involved.
possible binaries that starts faster (see #124556 (comment) and #124556 (comment))
The performance improvement would be around ~3ms. Wrapping is very widely used and debugging failed shebangs is a pain to debug. I don't think the binary wrapper is so widely tested that it is safe to assume that it just works for everything. Though it might be safe to assume that it works for ruby things.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, bash didn't fail to build there. It is most likely a splicing problem that a binary for a another platform is used and this shouldn't be changed by this PR. Also I think it is safe to assume that we cannot get bash out of bootstraping something. There is always some bash somewhere involved.
I am not saying to remove bash
from boostraping, but having less things to depend on piece of code that is fairly complex is sure a plus.
The performance improvement would be around ~3ms. Wrapping is very widely used and debugging failed shebangs is a pain to debug. I don't think the binary wrapper is so widely tested that it is safe to assume that it just works for everything. Though it might be safe to assume that it works for ruby things.
The debugging part is fair enough, however don't assume that the performance improvement is useless. Of course, for things that run only one time it is insignificant, however for things that runs in loop this can add pretty quickly.
Also, of course the wrapper is not widely tested (actually right now it is not used at all in nixpkgs). But starting with ruby and them extending it to other usages once we get more testing is something that I think should start doing eventually.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By branching here on platform, if someone using Linux adds a
--run
-flag (or another flag which is supported by makeWrapper but not makeBinaryWrapper), they could break the wrappers on macOS without noticing and without breaking them on Linux.
Good point but that also means that we can't use the wrapper for everything yet.
Also this PR rebuilds more things than cewl.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point but that also means that we can't use the wrapper for everything yet.
Yeah, but nobody is saying that we are going to switch everything to use this wrapper right now.
Also this PR rebuilds more things than cewl.
The PR title is incorrectly but the commit message is correct(-ish).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's such a pain to debug such wrappers. Here with Neovim, I opened for example ./results/cewl/bin/cewl
and opened the binary wrapper interpreter from the shebang, and saw immediately the text in the binary contents:
# ------------------------------------------------------------------------------------
# The C-code for this binary wrapper has been generated using the following command:
makeCWrapper /nix/store/vchcc0l18dj4pk07jv1z4qhd27lq0ni1-ruby-2.7.5/bin/ruby \
--set 'BUNDLE_GEMFILE' '/nix/store/9mbvhw5w5zm81ay0jf2jyx59g7a72zcr-gemfile-and-lockfile/Gemfile' \
--unset 'BUNDLE_PATH' \
--set 'BUNDLE_FROZEN' '1' \
--set 'GEM_HOME' '/nix/store/as2jwqvgcginmn13qzdqh5c8dyc27i6h-cewl-ruby-env/lib/ruby/gems/2.7.0' \
--set 'GEM_PATH' '/nix/store/as2jwqvgcginmn13qzdqh5c8dyc27i6h-cewl-ruby-env/lib/ruby/gems/2.7.0'
# (Use `nix-shell -p makeBinaryWrapper` to get access to makeCWrapper in your shell)
# ------------------------------------------------------------------------------------
So I don't feel in any way that this effects my wrappers debugging workflow, assuming I count on makeBinaryWrapper
that this environment is actually set before the wrapper executes the final binary, and for that we have makeBinaryWrapper
's golden tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The performance improvement would be around ~3ms.
Someone in a group that I participate confirmed those numbers on NixOS. A quick calculation here:
- 3ms * 1000 = 3s
- 3ms * 10000 = 30s
- 3ms * 100000 = 300s
I can definitively see some script calling a program multiple times in a loop hitting this number of calls, and eventually this becomes a very large overhead.
Yeah, maybe you could argue that a script calling a program this number of times is broken, and it should be converted to a program or etc, however I would argue that we should improve this if possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Result of 1 package failed to build:
9 packages built:
|
In general, we can merge such changes to a staging branch and use hydra builds to test that everything is functional. That might be a good idea when we'll want to make Python's wrappers binary, here perhaps it's not needed. |
I am quite in favor of using |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand the desire for stability and consistency, but the need for binary wrappers is a fact. If, worst case, we encounter a surprise, we'll learn something 🎉. +1 on this change.
On the topic of surprises, could you add a test? Even a basic test can rule out most packaging mistakes.
|
I am convinced now that we can slowly introduce binaryWrappers like this. They seem to be solid enough so far and I don't have a better idea to get them more tested and into the wild. If we encounter issues we just revert it and try to fix them. Let me quickly add a basic test for wrappedRuby to make sure the binary still executes and push it and then we are good to go here. |
@roberth Can you re-review? |
@ofborg test gitlab |
I guess the people here will be interested in #164163 |
This resolved for |
Fixes the following error on macOS when trying to run cewl:
Likely also fixes other ruby programs depending on ruby-modules/bundler-env for macOS. According to nixpkgs-review, this seems to affect the following packages:
Test it out:
env NIX_PATH=nixpkgs=https://github.com/bergkvist/nixpkgs/archive/macos-shebang.tar.gz \ nix-shell -p cewl --run "cewl --help"
Motivation for this change
nixpkgs uses bash wrapper scripts to modify environment variables that are used by executables. Sometimes these executables are interpreters, and placed in the shebang line of a script. On macOS, shebangs can't point to shell scripts, unlike on Linux.
pkgs.makeBinaryWrapper
is a recent addition to nixpkgs, which is an alternative topkgs.makeWrapper
. It solves this particular problem by generating and compiling C code instead of generating a shell script. That means you can put it in a shebang line on macOS. The runtime overhead is also less than that of bash wrappers - with some disadvantages being a larger wrapper file size, and less features (no--run <shell-command>
being the biggest one).Relevant issues:
Things done
sandbox = true
set innix.conf
? (See Nix manual)nix-shell -p nixpkgs-review --run "nixpkgs-review rev HEAD"
. Note: all changes have to be committed, also see nixpkgs-review usage./result/bin/
)nixos/doc/manual/md-to-db.sh
to update generated release notes